using System;
using System.Collections.Generic;
using UnityEngine;

namespace Pathfinding
{
    public class OctreeNode : IAStarNode<OctreeNode>, IComparable<OctreeNode>
    {
        private const float MinCubeSize = 50f; // 最小方格尺寸
        public OctreeNode Parent { get; set; } //父节点
        public float SelfCost { get; set; }
        public float GCost { get; set; }
        public float HCost { get; set; }
        public float FCost => GCost + HCost;

        public OctreeNode[] Children; //子节点
        public Bounds NodeCube; //用包围盒作为结点方块，方便后续检测
        public NodeType Type = NodeType.Normal;

        public OctreeNode(Bounds nodeCube, OctreeNode parent)
        {
            Parent = parent;
            NodeCube = nodeCube;
            SelfCost = 1;
        }

        public void Divide(Collider collider)
        {
            //因为是正方体，所以用一条边来判断尺寸即可
            if (NodeCube.size.x >= MinCubeSize)
            {
                // 子方块的半尺寸， 用半尺寸是因为构建Bounds需要
                var childHalfSize = NodeCube.size.x / 4;
                Children ??= new OctreeNode[8];
                for (var i = 0; i < 8; ++i)
                {
                    //0~7的二进制位结构恰好满足我们所需要的组合形式
                    Vector3 offset; //子节点偏移
                    offset.x = (1 & i) != 0 ? childHalfSize : -childHalfSize; //取二进制第0位
                    offset.y = (2 & i) != 0 ? childHalfSize : -childHalfSize; //取二进制第1位
                    offset.z = (4 & i) != 0 ? childHalfSize : -childHalfSize; //取二进制第2位
                    var childBounds = new Bounds(NodeCube.center + offset, 2 * childHalfSize * Vector3.one);
                    Children[i] ??= new OctreeNode(childBounds, this);
                    /*
                    进一步分裂前，先判断一下有没有遇到障碍物，没有就不要继续分裂了;
                    也可以再附带添加些其它检测条件，比如obj.layer等
                    */
                    if (childBounds.Intersects(collider.bounds))
                    {
                        Children[i].Divide(collider); // 每个子节点继续分裂
                    }
                }
            }
            else
            {
                Type = NodeType.Obstacles;
            }
        }

        //seeOne为true，则只查看分裂后的一个，否则查看所有分裂后的方块
        public void Draw(bool isSeeOne)
        {
            var drawColor = new Color(0, 1, 0, 0.2f);
            if (Type == NodeType.Obstacles)
                drawColor = new Color(1, 0, 0, 0.2f);
            Gizmos.color = drawColor;
            Gizmos.DrawWireCube(NodeCube.center, NodeCube.size);
            if (Children == null)
                return;
            foreach (var c in Children)
            {
                c.Draw(isSeeOne);
                if (isSeeOne)
                {
                    break;
                }
            }
        }

        public float GetDistance(OctreeNode otherNode)
        {
            return Vector3.Distance(NodeCube.center, otherNode.NodeCube.center);
        }

        public List<OctreeNode> GetSuccessors(object nodeMap)
        {
            var map = (Graph<OctreeNode, int>)nodeMap;
            return map.GetNeighbor(this);
        }

        public int CompareTo(OctreeNode other)
        {
            var res = FCost - other.FCost;
            if (res == 0)
                res = HCost - other.HCost;
            return (int)res;
        }
    }
}